package com.lindroid.gesturelock;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

import static com.lindroid.gesturelock.Point.STATE_ERROR;
import static com.lindroid.gesturelock.Point.STATE_PRESS;

/**
 * Created by Lindroid on 2017/6/1.
 */

public class GestureLockView extends View {
    //三维数组，用于保存点的位置
    private Point[][] points = new Point[3][3];
    //是否初始化过点
    private boolean isInited = false;
    private Bitmap bitmapPointNormal, bitmapPointPress, bitmapPointError;
    private float bitmapR;
    //起始坐标
    private float touchX, touchY;
    //当前是否处于绘制状态
    private boolean isDraw = false;
    //记录选中的点
    private ArrayList<Point> pointList = new ArrayList<>();
    //记录选中的点的序号(0~8)
    private ArrayList<Integer> passList = new ArrayList<>();
    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //按下时的画笔
    private Paint pressPaint = new Paint();
    //绘制错误时的连线
    private Paint errorPaint = new Paint();
    //绘制完成的监听
    private OnDrawFinishedListener listener;

    public GestureLockView(Context context) {
        super(context);
    }

    public GestureLockView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public GestureLockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!isInited) {
            init();
        }
        drawPoints(canvas);

        //动态绘制线
        if (pointList.size() > 0) {
            Point a = pointList.get(0);
            for (int i = 1; i < pointList.size(); i++) {
                Point b = pointList.get(i);
                drawLine(canvas, a, b);
                //将b点设为下一次的起始点
                a = b;
            }
            //在起始点和最后一个点上绘制线
            if (isDraw) {
                drawLine(canvas, a, new Point(touchX, touchY));
            }
        }
    }

    private void init() {
        //绘制连线的画笔
        pressPaint.setColor(Color.GREEN);
        pressPaint.setStrokeWidth(10);
        //绘制错误连线的画笔
        errorPaint.setColor(Color.RED);
        errorPaint.setStrokeWidth(10);

        bitmapPointError = BitmapFactory.decodeResource(getResources(), R.drawable.error_point);
        bitmapPointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.normal_point);
        bitmapPointPress = BitmapFactory.decodeResource(getResources(), R.drawable.press_point);
        //点的半径值（三张图片都是一样的，所以使用其中一张计算即可）
        bitmapR = bitmapPointError.getHeight() / 2;
        int width = getWidth();
        int height = getHeight();
        //offSet为手势图案在横轴或者竖轴方向上的偏移量
        int offset = Math.abs(width - height) / 2; //Math.abs取绝对值
        int offsetX, offsetY;
        int space;//每个圆点所在的小方格边长
        if (height < width) { //此时手机为横屏
            offsetX = offset;
            offsetY = 0;
            space = height / 4; //虽然有三个点，但是九宫格是在一个4x4的方格矩阵里面
        } else { //此时手机为竖屏
            offsetX = 0;
            offsetY = offset;
            space = width / 4;
        }
        //第一行的点（参数为点的圆心坐标，下同）
        points[0][0] = new Point(offsetX + space, offsetY + space);
        points[0][1] = new Point(offsetX + space * 2, offsetY + space);
        points[0][2] = new Point(offsetX + space * 3, offsetY + space);
        //第二行的点
        points[1][0] = new Point(offsetX + space, offsetY + space * 2);
        points[1][1] = new Point(offsetX + space * 2, offsetY + space * 2);
        points[1][2] = new Point(offsetX + space * 3, offsetY + space * 2);
        //第三行的点
        points[2][0] = new Point(offsetX + space, offsetY + space * 3);
        points[2][1] = new Point(offsetX + space * 2, offsetY + space * 3);
        points[2][2] = new Point(offsetX + space * 3, offsetY + space * 3);

        //表示已经初始化
        isInited = true;
    }

    /**
     * 触摸监听事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        touchX = event.getX();
        touchY = event.getY();
        int[] ij;
        int i;
        int j;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                resetPoints();//清除之前的痕迹
                ij = getSelectedPoint();
                if (ij != null) { //ij不为空，说明手指按在了点的有效范围
                    isDraw = true;
                    i = ij[0];
                    j = ij[1];
                    points[i][j].state = STATE_PRESS;
                    pointList.add(points[i][j]);
                    //二维数组转换为一维数组
                    passList.add(i * 3 + j);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (isDraw) {
                    ij = getSelectedPoint();
                    if (ij != null) {
                        i = ij[0];
                        j = ij[1];
                        if (!pointList.contains(points[i][j])) {
                            points[i][j].state = STATE_PRESS;
                            pointList.add(points[i][j]);
                            //二维数组转换为一维数组
                            passList.add(i * 3 + j);
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                boolean valid = false;
                if (listener != null && isDraw) {
                    valid = listener.OnDrawFinished(passList);
                }
                if (!valid) {
                    for (Point point : pointList) {
                        point.state = Point.STATE_ERROR;
                    }
                }
                isDraw = false;
                break;
        }
        //刷新界面
        postInvalidate();
        return true;
    }

    /**
     * 移除点
     */
    public void resetPoints() {
        pointList.clear();
        passList.clear();
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
                points[i][j].state = Point.STATE_NORMAL;
            }
        }
        //重新绘制
        postInvalidate();
    }

    public interface OnDrawFinishedListener {
        boolean OnDrawFinished(List<Integer> passList);
    }

    public void setOnDrawFinishedListener(OnDrawFinishedListener listener) {
        this.listener = listener;
    }

    /**
     * 获取选中的点
     *
     * @return
     */
    private int[] getSelectedPoint() {
        //当前手指按下的点
        Point pTouch = new Point(touchX, touchY);
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
                //表示手指点在圆点上
                if (points[i][j].distance(pTouch) < bitmapR) {
                    int[] result = new int[2];
                    result[0] = i;
                    result[1] = j;
                    return result;
                }
            }
        }
        return null;
    }

    /**
     * 绘制九宫格中的圆点
     *
     * @param canvas
     * 点的绘制不是从圆心开始的，而是从左上角开始的，所以要减去半径
     */
    private void drawPoints(Canvas canvas) {
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
                if (points[i][j].state == Point.STATE_NORMAL) { //常态下的点
                    canvas.drawBitmap(bitmapPointNormal, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
                } else if (points[i][j].state == STATE_PRESS) { //按下的点
                    canvas.drawBitmap(bitmapPointPress, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
                } else { //按错的点
                    canvas.drawBitmap(bitmapPointError, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
                }
            }

        }
    }

    /**
     * 绘制连线
     */
    private void drawLine(Canvas canvas, Point a, Point b) {
        if (a.state == STATE_PRESS) {
            canvas.drawLine(a.x, a.y, b.x, b.y, pressPaint);
        } else if (a.state == STATE_ERROR) {
            canvas.drawLine(a.x, a.y, b.x, b.y, errorPaint);
        }
    }
}
